const DEBUG = false;
const NOTIFY_STATE_DOCUMENT = Components.interfaces.nsIWebProgress.NOTIFY_STATE_DOCUMENT;
if (DEBUG){
	con = Components.utils;
	//con.reportError('msg');
}

function strpad(s,padChar,newLength){
	s = "" + s;
	while (s.length < newLength){
		s = "" + padChar + s;
	}
	return s;
}
function urlStrip(str, chr){
	pos = str.indexOf(chr);
	if (pos != -1){
		str = str.substring(0,pos);
	}
	return str;
}
function removeListener(tab){
	if(tab.listener){
		try {
			tab.removeProgressListener(tab.listener);
		}catch (e){ } //don't know how to check if already registered, so just try it
		delete tab.listener;
	}
}
function updateLeadZero(){
	var domain = this.gBrowser.currentURI.host;
	imageNav.removeCachedDomain(domain);
	blur();
}
var imageNav = {
	ACTION: {
		load: 0x1,
		load_folder: 0x2,
		build: 0x4,
		fetch: 0x8
	},
	FOLDER: {
		match: /(\d+)[^\d]*\/[^\/]*\d+[^\d]*(\?.*)?(#.*)?$/,
		replace: /[^\d]*\/[^\/]*\d+[^\d]*(\?.*)?(#.*)?$/
	},
	FILE: {
		match: /(\d+)[^\d]*(\?.*)?(#.*)?$/,
		replace: /[^\d]*(\?.*)?(#.*)?$/
	},
	DOMAIN: /^[a-z]+:\/{2,3}([^\/]+)(\/.*)$/,
	URICache: {
		list: new Array(5)
	},
	location: null,
	makeURI: function (aURL) {
	    var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
    	return ioService.newURI(aURL, null, null);
	},
	getcurrentTab: function (){
		return gBrowser.getBrowserAtIndex(gBrowser.mTabContainer.selectedIndex);
	},
	getLocation: function (tab){
		if (tab == undefined){
			tab = this.getcurrentTab();
		}
		return tab.contentDocument.location.href;
	},
	checkMatch: function(location, regExps){
		var path;
		location = this.makeURI(location);
		path = window.unescape(location.path);
		return path.match(regExps.match);
	},
	isImage: function (url){
		return !!url.match(/(\.jpg|\.jpeg).*(\?.*)?(#.*)?$/);
	},
	cacheEntryIsSet: function (location, direction, action){
		var tmp;
		uri_set = true;
		uri = null;
		location = this.fixLocation(location, action);
		domain = location[1];
		path = location[2];
		action = this.actionType(action);
		if (!(this.URICache[domain])){
			this.URICache[domain] = {};
			uri_set = false;
		}
		if (!uri_set || !(this.URICache[domain][path])){
			this.URICache[domain][path] = {};
			uri_set = false;
		}
		if (!uri_set || !(this.URICache[domain][path][action])){
			this.URICache[domain][path][action] = {};
			uri_set = false;
		}
		if (!uri_set || !(this.URICache[domain][path][action][direction])){
			this.URICache[domain][path][action][direction] = {};
			uri_set = false;
		}
		if (uri_set){
			uri = this.URICache[domain][path][action][direction];
		}
		return uri;
	},
	removeCachedDomain: function(domain){
		if (this.URICache[domain]){
			var tmp, prev;
			var len = this.URICache.list.length;
			for (i=0;i<len;i++){
				tmp = this.URICache.list[i];
				this.URICache.list[i] = prev;
				if (tmp == domain){
					break;
				}
				prev = tmp;
			}
			delete this.URICache[domain];
		}
	},
	updateURICacheList: function(domain){
		var tmp;
		var len = this.URICache.list.length;
		var prev = domain;
		for (i=len-1;i>=0;i--){
			tmp = this.URICache.list[i];
			this.URICache.list[i] = prev;
			if (tmp == domain){
				break;
			}
			prev = tmp;
		}
	},
	updateURICache: function(location, direction, action, newURI){
		var cacheLen,len;
		splitLocation = this.fixLocation(location, action);
		domain = splitLocation[1];
		path = splitLocation[2];
		if (this.URICache[domain]){
			this.updateURICacheList(domain);
		}else{
			this.URICache.list.push(domain);
			delete this.URICache[this.URICache.list.shift()];
		}
		this.cacheEntryIsSet(location,direction,action);
		action = this.actionType(action);
		this.URICache[domain][path][action][direction] = newURI;
	},
	fixLocation: function(location, action){
		var matches;
		if(action & this.ACTION.build){
			matches = location.match(/^(.*)\/[^\/]*$/);
			location = matches[1];
		}
		location = location.match(this.DOMAIN);
		return location;
	},
	actionType: function (action){
		action_type = 'file';
		if (action & this.ACTION.load_folder){
			var action_type = 'folder';
		}else if (action & this.ACTION.build){
			var action_type = 'build';
		}
		return action_type;
	},
	move: function (NavDirection,Action) {
		var L,oldNum,newNum,leadZero,ext,re,replacePath,trailingPath,newURI;
		var currentTab = this.getcurrentTab();
		this.location = L = cacheURI = this.getLocation(currentTab);
		newURI = this.cacheEntryIsSet(L,NavDirection,Action);
		if (newURI == null){
			this.updateURICache(cacheURI,NavDirection,Action,-1);
			if ((Action & this.ACTION.fetch) && !this.isImage(L)){
				return false; //Stop prefetching
			}
			re = this.FILE;
			if (Action & this.ACTION.load_folder){
				re = this.FOLDER;
			}
			leadZero = document.getElementById('leadZero').checked;
			if (replacePath = this.checkMatch(L,re)){
				if (Action & this.ACTION.load_folder){
					L = imageNav.move(1, this.ACTION.build); //Builds url with 01.jpg image url instead of 16.jpg, etc
				}
				oldNum = replacePath[1];
				if (oldNum == 1 && NavDirection < 0){
					if(!(Action & this.ACTION.build || Action & this.ACTION.fetch)){
						this.nextNotFound(Action, '', NavDirection);
					}
				}else{
					if (Action & this.ACTION.build){
						newNum = "1";
					}else{
						newNum = "" + (parseInt(oldNum,10) + NavDirection);
					}
					if (oldNum.length != newNum.length && //New number is shorter than old number
							(leadZero || //leadZero checkbox is checked
							((oldNum.length - 1) != newNum.length && //length has changed by more than 1 (e.g. 001 ended up as 1)
							!(Action & this.ACTION.load_folder)))){ //think this is due to folders rarely(never?) have leading 0s?
						newNum = strpad(newNum,'0',oldNum.length);
					}
					trailingPath = L.match(re.replace);
					newURI = L.replace(re.match,"" + newNum + trailingPath[0]);
					this.updateURICache(cacheURI,NavDirection,Action,newURI);
				}
			}else if (!(Action & this.ACTION.build || Action & this.ACTION.fetch)){
				this.nextNotFound(Action, '', NavDirection);
			}
		}
		return this.updateLocation(currentTab, L, Action, NavDirection, newURI);
	},
	Up: function() {
		var L,newURI;
		var currentTab = this.getcurrentTab();
		L = this.getLocation(currentTab);
		newURI = L.substring(0,L.substring(0,L.length-1).lastIndexOf('/')+1);
		currentTab.loadURI(newURI, null, this.makeURI(L));
	},
	updateLocation: function (tab, location, action, nav_direction, new_location){
		var e, tmp;
		var background_actions = this.ACTION.build | this.ACTION.fetch;
		if(new_location != -1){
			if (action & this.ACTION.fetch){
				if (new_location != null){
					var ImageNav_PrefetchService = Components.classes["@mozilla.org/prefetch-service;1"].getService(Components.interfaces.nsIPrefetchService);
					ImageNav_PrefetchService.prefetchURIForOfflineUse(this.makeURI(new_location), this.makeURI(location), null, true);
					return true;
				}else{
					return false;
				}
			}else if (action & this.ACTION.load || action & this.ACTION.load_folder){
				if (new_location != null){
					newURI = this.makeURI(new_location);
					httpRequestObserver.uri = newURI;
					httpRequestObserver.action = action;
					removeListener(tab);
					tab.listener = new stopRedirectListener(tab, action, this);
					httpRequestObserver.register();
					try {
						tab.addProgressListener(tab.listener);
					}catch (e){ } //don't know how to check if already registered, so just try it
					tab.loadURI(newURI.spec, null, this.makeURI(location));
				}else{
					return false;
				}
			}else if (action & this.ACTION.build){
				return new_location;
			}
		}else if (!(action & background_actions)){
			this.nextNotFound(action, '', nav_direction);
		}
	},
	nextNotFound: function (action, msg, direction){
		imgPos = 'Last';
		imgDir = 'Next';
		if (direction < 0){
			imgPos = 'First';
			imgDir = 'Previous';
		}
		if (!(action & this.ACTION.load_folder)){
			if (msg.length > 0){
				msg = ' (' + msg + ')';
			}
			msg = imgPos + ' image' + msg + '. ';
			if(this.checkMatch(this.location,this.FOLDER) === null){
				msg += ' No ' + imgDir.toLowerCase() + ' folder.';
				alert(msg);
			}else{
				msg += imgDir + ' folder?';
				if (confirm(msg)){
					this.move(direction,this.ACTION.load_folder);
				}
			}
		}else{
			alert('No ' + imgDir.toLowerCase() + ' folder');
		}
	}
};

/* Move to another file */
const STATE_START = Components.interfaces.nsIWebProgressListener.STATE_START;
const STATE_STOP = Components.interfaces.nsIWebProgressListener.STATE_STOP;
const STATE_REDIRECTING = Components.interfaces.nsIWebProgressListener.STATE_REDIRECTING;

/* Deal with redirects */
function stopRedirectListener(tab, action, callback) {
	this.stopRedirect = new stopRedirect(tab, action, callback);
	this.QueryInterface = function(aIID){
		if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
				aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
				aIID.equals(Components.interfaces.nsISupports)){
			return this;
		}
		throw Components.results.NS_NOINTERFACE;
	};
	this.onStateChange = function(aProgress, aRequest, aFlag, aStatus){
		if(aFlag & STATE_REDIRECTING){
			this.stopRedirect.redirecting();
		}
		if(aFlag & STATE_START){
			this.stopRedirect.startingStateChange();
		}
		return 0;
	};
	this.onLocationChange = function() {return 0;};
	this.onProgressChange = function() {return 0;};
	this.onStatusChange = function() {return 0;};
	this.onSecurityChange = function() {return 0;};
	this.onLinkIconAvailable = function() {return 0;};
}

function stopRedirect(tab, action, callback) {
	this.redirect = false;
	this.loading = false;
	this.tab = tab;
	this.action = action;
	this.callback = callback;
	this.redirecting = function(){
		this.redirect = true;
	};
	this.startingStateChange = function(){
		if (!this.loading){
			this.loading = true;
		}else if (	(this.tab !== null) &&
					(this.callback !== null) &&
					this.redirect){
			this.redirect = false;
			window.stop();
			removeListener(tab);
			this.callback.nextNotFound(this.action, 'redirect', 1);
		}
	};
};

/* Deal with 404 */
var httpRequestObserver = {
	action: null,
	uri: null,
	/*
	Very confusing
	Even after the unregister has been called, the observe method is still called. The destruction and subsequent check
	of this.uri is to stop the block of code being run more than once. Not sure if this is something stupid I'm doing :(
	*/
	observe: function(subject, topic, data){
		if (this.uri != null && this.uri.equals(subject.URI) && topic == "http-on-examine-response") {
			this.unregister();
			var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
			if (httpChannel.responseStatus == 404){
				this.uri = null;
				window.stop();
				imageNav.nextNotFound(this.action, '404', 1);
			}
		}
	},
	get observerService() {
		return Components.classes["@mozilla.org/observer-service;1"]
        	             .getService(Components.interfaces.nsIObserverService);
	},
	register: function(){
		this.observerService.addObserver(this, "http-on-examine-response", false);
	},
	unregister: function(){
		this.observerService.removeObserver(this, "http-on-examine-response");
	}
};

